/*
 * ScrollableContainer.cpp
 *
 *  Created on: 03-12-2010
 *      Author: Piotr
 */

#include "gui/ScrollableContainer.hpp"
#include "gui/ClipArea.hpp"
#include "gui/Control.hpp"
#include "utils/GLUtils.hpp"

ScrollableContainer::ScrollableContainer(Control* control) :
	Container(), verticalScrollBar(new ScrollBar(ScrollBar::VERTICAL)), horizontalScrollBar(
			new ScrollBar(ScrollBar::HORIZONTAL))
{
	this->control = boost::shared_ptr<Control>(control);
	setOpaque(false);
	add(verticalScrollBar);
	add(horizontalScrollBar);
}

void ScrollableContainer::fillAreaOutOfControlBounds(int horizontalShift, int verticalShift)
{
	geometry::Rectangle ctrlBounds = geometry::Rectangle(control.get()->getBounds());
	ctrlBounds.setPosition(Vector(horizontalShift, verticalShift) + ctrlBounds.getPosition());

	float ctrlLeft = ctrlBounds.getPosition().getX();
	float ctrlRight = ctrlBounds.getPosition().getX() + ctrlBounds.getSize().getWidth();
	float ctrlTop = ctrlBounds.getPosition().getY();
	float ctrlBottom = ctrlBounds.getPosition().getY() + ctrlBounds.getSize().getHeight();

	GLColor::BLACK.setGLState();
	GLUtils::drawRectangle(Vector(0, 0), getSize());
	GLUtils::drawRectangle(Vector(ctrlRight, 0.0f), getSize());
	GLUtils::drawRectangle(Vector(ctrlLeft, 0.0f), Dimension(ctrlRight, ctrlTop));
	GLUtils::drawRectangle(Vector(ctrlLeft, ctrlBottom), Dimension(ctrlRight, getSize().getHeight()));
}

void ScrollableContainer::draw(const ClipArea& clipArea)
{
	const Vector& viewAreaPosition = getViewAreaPosition();
	int horizontalShift = static_cast<int> (viewAreaPosition.getX());
	int verticalShift = static_cast<int> (viewAreaPosition.getY());

	fillAreaOutOfControlBounds(-horizontalShift, -verticalShift);

	glPushMatrix();
	translateOglCoordinatesToControlCoordinates(*(control.get()));
	glTranslatef(-horizontalShift, -verticalShift, 0.0f);

	ClipArea shiftedClipArea(clipArea);
	shiftedClipArea.shift(Vector(-horizontalShift, -verticalShift));
	ClipArea controlClipArea = shiftedClipArea.getUpdatedByControl(*control.get());
	controlClipArea.setGLClipArea();
	control.get()->draw(controlClipArea);

	glPopMatrix();

	setScrollBarsPlacements();

	clipArea.setGLClipArea();
	Container::draw(clipArea);
}

Control* ScrollableContainer::getDeepestControlAt(const Vector& point,
		Vector* pointInControlCoordinates)
{
	const Vector& translatedPointForScrollBars = point - getPosition();
	if(horizontalScrollBar->getBounds().contains(translatedPointForScrollBars)
			|| verticalScrollBar->getBounds().contains(translatedPointForScrollBars))
	{
		Control* candidate = getControlAt(translatedPointForScrollBars);

		Container* container = dynamic_cast<Container*> (candidate);
		if(container != NULL)
		{
			return container->getDeepestControlAt(translatedPointForScrollBars, pointInControlCoordinates);
		}
		pointInControlCoordinates->set(translatedPointForScrollBars - candidate->getPosition());
		return candidate;
	}

	const Vector& translatedPoint = point - getPosition() + getViewAreaPosition();
	Container* innerContainer = dynamic_cast<Container*> (control.get());
	if(innerContainer == NULL)
	{
		pointInControlCoordinates->set(translatedPoint - control.get()->getPosition());
		return control.get();
	}

	const Vector& innerContainerTranslatedPoint = translatedPoint - innerContainer->getPosition();
	Control* candidate = innerContainer->getDeepestControlAt(innerContainerTranslatedPoint,
			pointInControlCoordinates);
	return candidate;
}

void ScrollableContainer::setScrollBarsPlacements()
{
	const Dimension& size = getSize();

	const float barSize = ScrollBar::SIZE;

	geometry::Rectangle verticalScrollBarPlacement = geometry::Rectangle(Vector(size.getWidth()
			- barSize, 0.0f), Dimension(barSize, size.getHeight()));
	verticalScrollBar->setBounds(verticalScrollBarPlacement);

	const geometry::Rectangle& horizontalScrollBarPlacement = geometry::Rectangle(Vector(0.0f,
			size.getHeight() - barSize), Dimension(size.getWidth() - barSize, barSize));
	horizontalScrollBar->setBounds(horizontalScrollBarPlacement);
}

Vector ScrollableContainer::getViewAreaPosition() const
{
	float horizontalSpace = control.get()->getSize().getWidth() - getSize().getWidth()
			+ ScrollBar::SIZE;
	float verticalSpace = control.get()->getSize().getHeight() - getSize().getHeight()
			+ ScrollBar::SIZE;

	float yPos = verticalScrollBar->getScrolledPercent() * verticalSpace;
	float xPos = horizontalScrollBar->getScrolledPercent() * horizontalSpace;

	return Vector(xPos, yPos);
}

